<template>
  <div class="formX">
    <el-form ref="formDom" v-bind="$attrs" :model="props.model"
             scroll-to-error :label-width="labelWidthD" :rules="props.rules">
      <el-form-item v-for="(item,index) in itemListD" :key="item.prop"
                    :style="Object.assign({width: item.itemWidth},item.style || {})"
                    v-bind="item" :class="{noLabelItem: item.noLabel,[item.class]: true}">

        <!-- region label插槽 -->
        <template #label>
          <slot v-if="item.slotLabel" :name="item.slotLabel"
                :data="item" :index="index" :model="props.model"/>

          <div class="defaultLabel" v-else>
            <popover-x :disabled="!item.havePopover" noBody
                       v-bind="item.popoverOptions">
              <template #default>
                <span class="popoverLabel" :class="{noLabel: item.noLabel,[item.labelClass]: true,isActivePopover: item.havePopover}">
                  {{
                    typeof item.label == 'function' ? item.label(item, props.model) : getI18nTextFn(item.label, item.i18Text)
                  }}
                  <span v-if="item.icon" class="iconLabel" :class="item.iconClass">
                    <icon-x :type="item.icon" :size="item.iconSize"/>
                  </span>
                </span>
              </template>
              <template #content>
                <div class="labelPopover">
                  {{
                    typeof item.popoverOptions?.content == 'function' ?
                        item.popoverOptions?.content(item, props.model) :
                        item.popoverOptions?.content
                  }}
                </div>
              </template>
            </popover-x>
          </div>

        </template>
        <!-- endregion -->


        <!-- region 开启错误文字插槽 -->
        <template v-if="item.slotError" #error>
          <slot :name="item.slotError" :data="item" :index="index" :model="props.model"></slot>
        </template>
        <!-- endregion -->


        <!-- region 内容插槽 -->
        <template #default>
          <!-- region 开启自定义插槽 -->
          <slot v-if="item.slot" :name="item.slot" :data="item" :index="index" :model="props.model">
            <span :class="{noLabel: item.noLabel}">
              {{
                typeof item.label == 'function' ?
                    item.label(item, props.model) :
                    getI18nTextFn(item.label, item.i18Text)
              }}
            </span>
          </slot>
          <!-- endregion -->

          <!-- region 输入框 -->
          <div v-else-if="item.type == 'input'" class="inputDefault"
               :style="{width: item.width}">

            <!-- placeholder如果是函数，可能是为了国际化 -->
            <el-input class="inputValue xValue" v-bind="item.options" :class="item.options.class"
                      @input="props.model[item.prop] = item.options.inputFn(props.model[item.prop])"
                      :placeholder="typeof item?.options?.placeholder == 'function'?
                                    item.options.placeholder(item,props.model):item?.options?.placeholder"
                      v-model="props.model[item.prop]" @change="changeFn(item)">
              <template #prefix v-if="item.options.prefixSlot">
                <slot :name="item.options.prefixSlot" :model="props.model"/>
              </template>

              <template #suffix v-if="item.options.suffixSlot">
                <slot :name="item.options.suffixSlot" :model="props.model"/>
              </template>

              <template #prepend v-if="item.options.prependSlot">
                <slot :name="item.options.prependSlot" :model="props.model"/>
              </template>

              <template #append v-if="item.options.appendSlot">
                <slot :name="item.options.appendSlot" :model="props.model"/>
              </template>
            </el-input>
            <!-- 内容尾部插槽 -->
            <slot v-if="item.slotMore" class="slotMore" :name="item.slotMore" :item="item" :model="props.model"/>
          </div>
          <!-- endregion -->


          <!-- region 日期选择器 -->
          <div class="dateDefault" v-else-if="item.type == 'date'"
               :style="{width: item.width}">
            <el-date-picker class="dateValue xValue" v-bind="item.options"
                            v-model="props.model[item.prop]"

                            :placeholder="typeof item?.options?.placeholder == 'function'?
                                    item?.options?.placeholder(item,props.model):item?.options?.placeholder"

                            :start-placeholder="typeof item?.options?.startPlaceholder == 'function'?
                                    item?.options?.startPlaceholder(item,props.model):
                                    item?.options?.startPlaceholder"

                            :end-placeholder="typeof item.options?.endPlaceholder == 'function'?
                                    item.options?.endPlaceholder(item,props.model):
                                    item?.options?.endPlaceholder"

                            @change="changeFn(item)"/>

            <!-- 内容尾部插槽 -->
            <slot v-if="item.slotMore" class="slotMore" :name="item.slotMore" :model="props.model"/>
          </div>
          <!-- endregion -->

          <!-- region 选择器 -->
          <div class="selectDefault" v-else-if="item.type == 'select'"
               :style="{width: item.width}">
            <el-select class="selectValue xValue" v-bind="item.options"
                       v-model="props.model[item.prop]"
                       :placeholder="typeof item?.options?.placeholder == 'function'?
                                    item?.options?.placeholder(item,props.model):item?.options?.placeholder"
                       @change="changeFn(item)">
              <el-option v-for="sItem in item.data" :key="sItem.value"
                         :value="sItem.value"
                         :label="typeof sItem.label == 'function'?sItem.label(sItem,props.model):getI18nTextFn(sItem.label,sItem.i18Text)"/>

            </el-select>
            <!-- 内容尾部插槽 -->
            <slot v-if="item.slotMore" class="slotMore" :name="item.slotMore" :model="props.model"/>
          </div>
          <!-- endregion -->

          <!-- region 树形选择器 -->
          <div class="selectDefault" v-else-if="item.type == 'tree'"
               :style="{width: item.width}">
            <el-tree-select class="selectValue xValue" v-bind="item.options"
                            v-model="props.model[item.prop]" :data="item.data"
                            :placeholder="typeof item?.options?.placeholder == 'function'?
                                    item?.options?.placeholder(item,props.model):item?.options?.placeholder"
                            @change="changeFn(item)"/>
            <!-- 内容尾部插槽 -->
            <slot v-if="item.slotMore" class="slotMore" :name="item.slotMore" :model="props.model"/>
          </div>
          <!-- endregion -->


          <!-- region 开关 -->
          <div class="switchDefault" v-else-if="item.type == 'switch'"
               :style="{width: item.width}">
            <switch-x-v3 class="switchValue xValue" v-model="props.model[item.prop]"
                         v-bind="item.options"/>
          </div>
          <!-- endregion -->

          <!-- region 数字输入框 -->
          <div class="inputNumDefault" v-else-if="item.type == 'inputNum'"
               :style="{width: item.width}">
            <el-input-number class="inputNumValue xValue" v-model="props.model[item.prop]"
                             v-bind="item.options"/>
          </div>
          <!-- endregion -->


        </template>
        <!-- endregion -->

      </el-form-item>
    </el-form>
  </div>
</template>

<script setup>
import {computed, watch, ref} from 'vue'
import PopoverX from "./xhykPopover.vue";
import SwitchXV3 from './xhykSwitch.vue'
import IconX from './xhykIcon.vue'
import {
  ElForm, ElFormItem,
  ElInput, ElDatePicker,
  ElSelect, ElOption,
  ElInputNumber,
  ElTreeSelect,
} from 'element-plus'

const props = defineProps({
  // 表单对象
  model: {
    type: Object,
    default: {}
  },

  // 表单item列表。注意需要ref()等封装
  itemList: {
    type: Object,
    default: [
      {
        label: '测试1',
        // 绑定的对象的字段
        prop: '',
        // 输入框宽度
        width: '100%',

        // region 插槽
        // 注意有值时，下面的slotMore会失效
        slot: undefined,
        // 输入框尾部的自定义内容插槽
        slotMore: undefined,
        // label的插槽名称
        slotLabel: undefined,
        // 错误插槽名称
        slotError: undefined,
        // endregion

        // label宽度。优先级高于全局
        labelWidth: 70,
        // label宽度为0，优先级高于局部labelWidth
        noLabel: false,

        // 总item的宽度
        itemWidth: undefined,

        // 是否开启这条item
        show: false,
        // 类型：输入框input，选择器select，日期选择器date，开关switch，数字输入框inputNum等
        type: '',

        // item的类名。line，
        class: '',

        // region label左侧图标
        // label左侧图标的类名
        iconClass: '',

        // label左侧的图标
        icon: undefined,

        // label左侧图标的大小
        iconSize: undefined,
        // endregion

        // 对应类型的组件配置
        options: {
          // 输入内容后的内容显示前的回调函数
          inputFn: undefined,
          // 内容发生改变时回调
          change: undefined,
        },

        // region 标题弹窗提示信息
        popoverOptions: undefined,
        // popoverOptions: {
        // content: '角色的菜单权限的分类，仅父级菜单有效，多个用,分开',
        // effect: 'dark',
        // },
        // endregion


      }
    ]
  },

  // 全局输入框宽度
  inputWidth: {
    type: String,
    default: '100%'
  },

  // labelWidth，已将字符串或数值类型进行转化
  labelWidth: {
    type: [String, Number],
    default: 80
  },

  // item的类名
  itemClass: {
    type: String,
    default: undefined
  },

  // region 发送验证

  // 规则
  rules: {
    type: Object,
    default: undefined
  },

  // 验证通过回调函数
  pass: {
    type: Function,
    default: pass => {
      return true
    }
  },

  // 验证失败时回调函数
  error: {
    type: Function,
    default: pass => {
      return false
    }
  },

  // 是否发送提交
  toSend: {
    type: Boolean,
    default: false
  },

  // 通过/失败回调时的额外参数
  callBackQuery: {
    type: Object,
    default: {}
  },

  // endregion


  // region 国际化
  // 国际化函数
  i18Fn: {
    type: Function,
    // item：itemList的数据。which：来源（label,select,selectPlaceholder,inputPlaceholder）
    default: (item, which, formData) => {
      if (which == 'label' || which == 'select') {
        return item.label ? item.label : ''
      } else {
        return item.options?.placeholder ? item.options?.placeholder : ''
      }
    }
  },
  // endregion

})


// haveSend：是否发送成功
const emit = defineEmits(['update:toSend', 'haveSend', 'change'])

// region 辅助工具

// 将字符串或数值转换带单位的字符串
function getPx(text, defaultPx = 'px') {
  let tempList = ['px', 'rpx', 'vw', 'vh', '%', 'em', 'auto']
  if (typeof text == 'string') {
    const havePx = tempList.find(row => text.includes(row))
    if (havePx) {
      return text
    } else {
      return `${parseFloat(text)}${defaultPx}`
    }
  } else if (typeof text == 'number') {
    let temp = parseFloat(text)
    return `${temp}${defaultPx}`
  }
  return text
}

// endregion


// region form表单字段配置

// labelWidth宽度，转换字符串或数值类型
const labelWidthD = computed(() => {
  return getPx(props.labelWidth)
})

// endregion


// region 处理item列表字段

// formItem列表
const itemListD = ref([])

// 处理formItem
const makeItemListFn = () => {
  let result = []
  props.itemList?.forEach((item, index) => {
    let temp = {}
    // 是否显示
    let showThis = item.show
    if (showThis == false) {
      return;
    }

    for (let key in item) {
      temp[key] = item[key]
    }

    // region 组件属性配置
    if (temp.options == undefined) {
      temp.options = {}
    }

    // 默认显示验证错误信息
    if (temp.showMessage == undefined) {
      temp.showMessage = true
    }

    // labelWidth字符串或数值类型进行转化
    temp.labelWidth = getPx(temp.labelWidth) || getPx(props.labelWidth)
    if (temp.labelWidth == '0px') {
      temp.noLabel = true
    }

    if (temp.noLabel) {
      temp.labelWidth = '0px'
    }

    // 输入框宽度
    temp.width = getPx(temp.width)

    if (temp.width == undefined) {
      temp.width = '100%'
    }

    // formItem的宽度
    temp.itemWidth = getPx(temp.itemWidth)

    // 类名
    if (temp.class == undefined) {
      temp.class = props.itemClass
    }

    // 国际化
    if (typeof temp.i18Fn != 'function') {
      temp.i18Fn = props.i18Fn
    }

    // endregion

    // region 输入框
    if (temp.type == 'input' || temp.type == undefined) {
      temp.type = 'input';
      if (!temp.options.placeholder) {
        temp.options.placeholder = `请输入${temp.label}`
      }
      temp.options.clearable = temp.options.clearable == undefined ? true : temp.options.clearable;

      // 输入框回调函数
      if (typeof temp.options.inputFn != 'function') {
        temp.options.inputFn = (text) => {
          return text
        }
      }

    }
    // endregion

    // region 日期选择器
    if (temp.type == 'date') {
      if (!temp.options.placeholder) {
        temp.options.placeholder = `请选择${temp.label}`
      }
      if (!temp.options.startPlaceholder) {
        temp.options.startPlaceholder = `请选择开始时间`
      }
      if (!temp.options.endPlaceholder) {
        temp.options.endPlaceholder = `请选择结束时间`
      }
      if (!temp.options.format) {
        // YYYY-MM-DD hh:mm:ss
        temp.options.format = 'YYYY-MM-DD'
        if (temp.options.type?.includes('time')) {
          temp.options.format = 'YYYY-MM-DD HH:mm:ss'
        }
      }
      if (!temp.options.valueFormat) {
        temp.options.valueFormat = temp.options.format
      }
      if (temp.options.disabledDate == undefined && temp.onlyAfter) {
        temp.options.disabledDate = (event) => {
          // console.log(event)
        }
      }
      temp.options.clearable = temp.options.clearable == undefined ? true : temp.options.clearable;
    }

    // endregion

    // region 下拉框
    if (temp.type == 'select') {
      if (!temp.options.placeholder) {
        temp.options.placeholder = `请选择${temp.label}`
      }
      // 默认不将下拉列表插入至 body 元素
      if (temp.options.teleported == undefined) {
        temp.options.teleported = false
      }
      temp.options.clearable = temp.options.clearable == undefined ? true : temp.options.clearable;
    }

    // endregion

    // region label是否弹窗
    temp.havePopover = temp.popoverOptions != undefined
    if (!temp.havePopover) {
      temp.popoverOptions = {noBody: true}
    }
    // endregion

    result.push(temp)
  })
  itemListD.value = result

}

watch(() => props.itemList, (newV, oldV) => {
  makeItemListFn()
}, {immediate: true, deep: true})

// endregion


// region 发送表单验证

// 表单对象
const formDom = ref({})

// 验证通过时调用
const toSendFn = async () => {
  // 访问前回调函数的结果。true时暂停发送
  if (props.rules) {
    // 需要验证
    await formDom.value.validate(pass => {
      if (pass) {
        props.pass(props.model, props.callBackQuery)
        emit('haveSend', true)
      } else {
        typeof props.error == 'function' && props.error(props.model, props.callBackQuery)
        emit('haveSend', false)
      }
    })
  } else {
    // 不需要验证
    emit('haveSend', true)
    props.pass(props.model, props.query)
  }

  // 发送后变成false
  emit('update:toSend', false)
}

// 发送提交
watch(() => props.toSend, newV => {
  if (newV) {
    toSendFn()
  }
})
// endregion


// region 事件

// 发生改变时
const changeFn = (item) => {
  if (typeof item.options.change == 'function') {
    item.options.change(item)
  }
  emit("change", item)
}

// endregion


// region 处理国际化

const getI18nTextFn = (oldText, i18nText) => {
  if (typeof window.$t == 'function' && i18nText) {
    return window.$t(i18nText)
  } else {
    return oldText
  }
}

// endregion


// 清除验证
const clearValidate = () => {
  formDom.value?.clearValidate()
}

defineExpose({
  clearValidate,
  toSendFn,
  formDom
})


</script>

<style scoped lang='scss'>
.formX {
  .el-form-item {
    align-items: center;
  }
}


// region 没有label
:deep(.noLabelItem) {
  .el-form-item__label {
    display: none;
  }
}

.noLabel {
  display: none;
}

// endregion


:deep(.xValue) {
  width: 100% !important;
  box-sizing: border-box;
}


// region 自定义各种情况的类名

.line {
  display: inline-flex;
}


.lineHalf {
  @extend .line;
  width: 50%;
}

.flex {
  display: flex;
  width: 100%;
}

// label左上对齐。
.labelTopFlex {
  display: flex;
  width: 100%;
  align-items: flex-start !important;

  .el-form-item__label {
    align-items: flex-start;
  }
}

.inputTextaresNoSize {
  @extend .labelTopFlex;

  :deep(.el-textarea__inner) {
    resize: none;
  }
}

// endregion

.defaultLabel {
  display: flex;
  align-items: center;

  .popoverLabel {
    display: inline-flex;
    align-items: center;
  }

  .iconLabel {
    margin-left: 4px;
    display: inline-flex;
    justify-content: center;
    align-items: center;
  }
}


.isActivePopover {
  cursor: pointer;
  &:hover {
    color: var(--menuSysColor,#409EFF);
  }
}

.inputDefault {
  display: flex;
  align-items: center;

  .xValue {
    flex: 1 1 auto;
  }

  .slotMore {
    flex: 0 0 auto;
  }
}
</style>